;; The following macro was suggested by Chris Vine on the Guile Scheme user
;; mailing list.

;; Comments added by me.

;; Warning: Apparently the following transcript posted by Mark H. Weaver on the
;; Guile Scheme user mailing list shows, that this macro is not hygienic:

;; ==========
;; TRANSCRIPT
;; ==========
#|
--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> (define-syntax ->
  (lambda (x)
    (syntax-case x ()
      [(k exp0 . exps)
       (let* ([reversed (reverse (cons (syntax->datum #'exp0)
                                       (syntax->datum #'exps)))]
              [out (let loop ([first (car reversed)]
                              [rest (cdr reversed)])
                     (if (null? rest)
                         first
                         (let ([func (car first)]
                               [args (cdr first)])
                           (append `(,func ,@args)
                                   (list (loop (car rest) (cdr rest)))))))])
         (datum->syntax #'k out))])))
scheme@(guile-user)> (define t 'global-t)
scheme@(guile-user)> (define-syntax-rule (foo x)
                       (-> x (format #t "[t=~A] ~A\n" t)))
scheme@(guile-user)> (let ((t 'inner-t)) (foo t))
[t=global-t] global-t
$1 = #t
scheme@(guile-user)>
--8<---------------cut here---------------end--------------->8---

I recommend reformulating the -> macro using 'syntax-rules' as follows:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> (define-syntax ->
                       (syntax-rules ()
                         ((-> exp)
                          exp)
                         ((-> exp ... (op args ...))
                          (op args ... (-> exp ...)))))
scheme@(guile-user)> (let ((t 'inner-t)) (foo t))
[t=global-t] inner-t
$8 = #t
scheme@(guile-user)>
--8<---------------cut here---------------end--------------->8---
|#


(define-syntax ~>
  ;; When using `syntax-case`, we need to wrap it in a lambda.
  (lambda (x)
    ;; Look at the input `x` and define no literals.
    (syntax-case x ()
      ;; There is only one case, which differentiates between the thing going
      ;; through the pipe represented by `k`, the first expression `exp0`, and
      ;; remaining expressions as `exps`.
      [(k exp0 . exps)

       (let*
           ([reversed
             ;; We are reversing the given expressions, because the first one
             ;; given is the inner-most in the result of the macro. The
             ;; applications of expressions of the reversed list can be nested
             ;; one after another until none remains and everything is applied
             ;; to `k`.
             (reverse
              ;; Make a normal list out of the pattern matched parts, so that it
              ;; can be reversed using the reverse function.
              (cons (syntax->datum #'exp0)
                    (syntax->datum #'exps)))]
            ;; The output of the macro is computed by nesting the operations
            ;; contained within the expressions from the reversed list of
            ;; expressions.
            [out
             ;; We use a named let as loop.
             (let loop
                 ;; For an initial value we extract the first expression from
                 ;; the reversed list of expressions. It will be the outermost
                 ;; wrapping call in the result of the macro.
                 ;; In further iterations the loop will one by one move inwards
                 ;; towards the first applied expression in the result of the
                 ;; macro.
                 ([first (car reversed)]
                  ;; The next iteration will work with the rest of the
                  ;; expressions. Typical list eater behavior.
                  [rest (cdr reversed)])
               ;; We check whether there are more expressions in the rest of the
               ;; expressions, to know when to not recur by calling `loop`
               ;; again.
               (if (null? rest)
                   ;; In case there are no remaining expressions in the rest of
                   ;; expressions to be applied to the macro input, we only need to
                   ;; apply the current `first` expression.
                   first
                   ;; In case there are still expressions, which need to be
                   ;; applied, we split up the procedure and the already given
                   ;; arguments to the procedure, so that we can insert the `k`
                   ;; into the arguments, changing what the procedure is applied
                   ;; to.
                   (let ([func (car first)]
                         [args (cdr first)])
                     ;; To the list that is the procedure and its arguments we
                     ;; append whatever is produced by the recursive calls of
                     ;; the `loop`, thereby wrapping the result of further
                     ;; iterations with the procedure call.
                     (append `(,func ,@args)
                             ;; `append` requires a list, so we need to build a
                             ;; list. Since this happens at read-time, the usage
                             ;; of append will not affect the performance at
                             ;; runtime.
                             (list
                              ;; Here we recur and use the rest of the list.
                              (loop
                                ;; The next iteration's first expression is the
                                ;; first of the rest of expressions
                               (car rest)
                               ;; The tail of the rest of expressions will make
                              ;; the next iteration's rest of expressions.
                               (cdr rest)))))))])
         ;; Wrap `out`, within the lexical context corresponding to the
         ;; identifier `k`. I think this is needed to give out the same scope
         ;; and as the original input to the macro had. It will mean, that in
         ;; that scope the same things are visible and invisible and `out`
         ;; cannot access anything not accessible from where the macro was
         ;; called.
         (datum->syntax #'k out))])))
